const { ccclass, property, requireComponent, executeInEditMode } = cc._decorator;

@ccclass
@executeInEditMode
@requireComponent(cc.Graphics)
export default class BezierEx extends cc.Component {
    @property(cc.Integer)
    solidLineWidth = 10;

    @property(cc.Integer)
    DashedLineWidth = 5;

    @property(cc.Graphics)
    graphics: cc.Graphics = null;

    @property(cc.Node)
    pathRoot: cc.Node = null;

    @property(cc.Node)
    controlRoot: cc.Node = null;

    @property(cc.Boolean)
    isRing = false;

    @property(cc.Boolean)
    isDrawOnLoad = false;

    @property(cc.Boolean)
    runInEditor = false;

    @property(cc.Boolean)
    isRealtime = false;

    get pathData(){
        return this.pathRoot.children.map((v)=>{return cc.v2(v.x, v.y)});
    }

    get bezierData(){
        return this._bezierData;
    }

    _bezierData : [cc.Vec2, cc.Vec2, cc.Vec2][]= [];

    onLoad() {
        if (CC_EDITOR && !this.runInEditor)
            return;


        this.graphics = this.graphics || this.node.getComponent(cc.Graphics);

        if (this.isDrawOnLoad) {
            this.drawLines();
        }
    }

    clear() {
        this.graphics.clear();
    }

    drawLines() {
        const pathNodes = this.pathRoot.children;
        const controlNodes = this.controlRoot.children;
        const len = pathNodes.length;
        this._bezierData = [];

        for (let i = 1; i < len; ++i) {
            const p0 = pathNodes[i - 1];
            const c1 = controlNodes[i - 1];
            const p1 = pathNodes[i];
            this.drawLine(p0, c1, p1);
        }

        if (this.isRing) {
            const p0 = pathNodes[len - 1];
            const c1 = controlNodes[len - 1];
            const p1 = pathNodes[0];
            this.drawLine(p0, c1, p1);
        }
    }

    drawLine(p0: cc.Node, c1: cc.Node, p1: cc.Node) {
        const start = cc.v2(p0.x, p0.y);
        const control = cc.v2(c1.x, c1.y);
        const end = cc.v2(p1.x, p1.y)
        this.drawBezierByDashedLine(this.graphics, start , control, end,
            [this.solidLineWidth, this.DashedLineWidth]);

        this._bezierData.push([start, control, end]);
    }

    /**
     * 使用虚线绘制二次贝塞尔曲线
     * @param ctx           cc.Graphics
     * @param startPoint    起始点
     * @param controlPoint  控制点坐标
     * @param endPoint      终点坐标
     * @param segments[]    [实线宽度， 虚线宽度]   
     */
    drawBezierByDashedLine(ctx: cc.Graphics, startPoint: cc.Vec2, controlPoint: cc.Vec2, endPoint: cc.Vec2, segments: number[] = [10, 5]) {
        let dashLineWidth = 200;
        let currentLineWidth = 0;
        let currentLineLength = segments[0];
        let isSolidLine = true;

        ctx.moveTo(startPoint.x, startPoint.y);

        while (currentLineWidth < dashLineWidth) {
            if (currentLineWidth + currentLineLength > dashLineWidth) {
                currentLineLength = dashLineWidth - currentLineWidth;
            }

            let t = currentLineWidth / dashLineWidth;
            let x = (1 - t) * (1 - t) * startPoint.x + 2 * (1 - t) * t * controlPoint.x + t * t * endPoint.x;
            let y = (1 - t) * (1 - t) * startPoint.y + 2 * (1 - t) * t * controlPoint.y + t * t * endPoint.y;

            if (isSolidLine) {
                ctx.lineTo(x, y);
                ctx.stroke();
            }
            else {
                ctx.moveTo(x, y);
            }

            currentLineWidth += currentLineLength;
            isSolidLine = !isSolidLine;
            currentLineLength = isSolidLine ? segments[0] : segments[1];
        }
    }

    protected update(dt: number): void {
        if (CC_DEBUG && this.isRealtime) {
            this.clear();
            this.drawLines();
        }
    }
}
